﻿using ArtMath.Attribute;
using ArtMath.Component.Line;
using ArtMath.Component.Point;
using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.PointCollection;
using ArtMath.Resource;
using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace ArtMath.Component.ConicalSection
{
    /// <summary>
    /// 焦点准线双曲线
    /// </summary>
    [Serializable]
    public class FocusDirectrixHyperbola : HyperbolaBase
    {
        StraightLineBase directrix;
        double c;
        bool unlimit = false;
        public FocusDirectrixHyperbola(SketchPad pad, string name, StraightLineBase zx, bool addToList = true) : base(pad, name, ToolTypeName.jdzxsqx, addToList)
        {
            InPoints = new PointBase[2];
            Directrix = zx;
            OrderIndex = Directrix == null ? 0 : 1;
        }
        public override bool PointsHaveConstraint
        {
            get { return true; }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("Directrix"), TypeConverter(typeof(ExpandableObjectConverter))]
        public StraightLineBase Directrix
        {
            get { return directrix; }
            set
            {
                directrix?.OutChildren_Line.Remove(this);
                directrix = value;
                directrix?.OutChildren_Line.Add(this);
            }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("{0}1", "Focus")]
        public PointBase FocusPoint1
        {
            get { return InPoints[0]; }
            set { SetPoint(0, value); }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("{0}2", "Focus")]
        public PointBase FocusPoint2
        {
            get { return InPoints[1]; }
            set { SetPoint(1, value); }
        }
        public override PointD Focus1
        {
            get
            {
                if (FocusPoint1 != null) return FocusPoint1.ToPointD();
                return base.Focus1;
            }
        }
        public override PointD NewFocus1
        {
            get
            {
                if (FocusPoint1 != null) return FocusPoint1.NewXY;
                return base.NewFocus1;
            }
        }
        public override PointD PFocus1
        {
            get
            {
                if (FocusPoint1 != null) return FocusPoint1.Pxy;
                return base.PFocus1;
            }
        }
        public override PointD Focus2
        {
            get
            {
                if (FocusPoint2 != null) return FocusPoint2.ToPointD();
                return base.Focus2;
            }
        }
        public override PointD NewFocus2
        {
            get
            {
                if (FocusPoint2 != null) return FocusPoint2.NewXY;
                return base.NewFocus2;
            }
        }
        public override PointD PFocus2
        {
            get
            {
                if (FocusPoint2 != null) return FocusPoint2.Pxy;
                return base.PFocus2;
            }
        }
        public override double NewFocusAngle
        {
            get
            {
                return FocusPoint1.NewXY.GetAngle(FocusPoint2.NewXY);
            }
        }
        public override double FocalHalfLength
        {
            get
            {
                return c;
            }
        }
        public override double NewFocalHalfLength
        {
            get
            {
                return FocusPoint1.NewXY.DistanceTo(FocusPoint2.NewXY) / 2;
            }
        }
        public override PointD NewCenter
        {
            get
            {
                return (FocusPoint1.NewXY + FocusPoint2.NewXY) / 2;
            }
        }
        public override PointD NewPCenter
        {
            get
            {
                return (FocusPoint1.NewPxy + FocusPoint2.NewPxy) / 2;
            }
        }
        public override double NewImaginaryAxisHalfLength
        {
            get
            {
                double p = Math.Min(FocusPoint1.NewXY.DistanceTo(Directrix.NewA, Directrix.NewB, Directrix.NewC),
                    FocusPoint2.NewXY.DistanceTo(Directrix.NewA, Directrix.NewB, Directrix.NewC));
                return Math.Sqrt(p * NewFocalHalfLength);
            }
        }
        public override double NewRealAxisHalfLength
        {
            get
            {
                double b = NewImaginaryAxisHalfLength, c = NewFocalHalfLength;
                return Math.Sqrt(c * c - b * b);
            }
        }
        //public override bool Visible
        //{
        //    get
        //    {
        //        return base.Visible;
        //    }

        //    set
        //    {
        //        base.Visible = value;
        //        if (Directrix != null)
        //        {
        //            Directrix.Visible = Visible;
        //        }
        //    }
        //}
        //public override bool Selected_
        //{
        //    get
        //    {
        //        return base.Selected_;
        //    }

        //    set
        //    {
        //        base.Selected_ = value;
        //        if (Directrix != null)
        //        {
        //            Directrix.Selected_ = Selected_;
        //        }
        //    }
        //}
        //public override StyleState StyleState_
        //{
        //    get
        //    {
        //        return base.StyleState_;
        //    }

        //    set
        //    {
        //        base.StyleState_ = value;
        //        if (Directrix != null)
        //        {
        //            Directrix.StyleState_ = StyleState_;
        //        }
        //    }
        //}
        public override void InChildrenIterator(Predicate<ShapeBase> act)
        {
            if (act == null) return;
            if (Directrix != null)
            {
                if (!act(Directrix)) return;
            }
            base.InChildrenIterator(act);
        }
        //protected override void SetChildrenLinesColor()
        //{
        //    if (Directrix != null)
        //    {
        //        Directrix.LineColor = LineColor;
        //    }
        //    base.SetChildrenLinesColor();
        //}
        //protected override void SetChildrenLinesDashStyle()
        //{
        //    if (Directrix != null)
        //    {
        //        Directrix.LineDashStyle = LineDashStyle;
        //    }
        //    base.SetChildrenLinesDashStyle();
        //}
        //protected override void SetChildrenLinesWidth()
        //{
        //    if (Directrix != null)
        //    {
        //        Directrix.LineWidth.SetExpression(LineWidth.Expression);
        //    }
        //    base.SetChildrenLinesWidth();
        //}
        //public override StraightLineBase[] GetStraightLines()
        //{
        //    return new StraightLineBase[] { Directrix };
        //}
        public override void ComponentChanged(ShapeBase component)
        {
            c = NewFocalHalfLength;
            base.ComponentChanged(component);
        }
        protected override void PointMoving_AsOutChildrenLimit(PointBase movePoint)
        {
            int movcnt = CountMovablePoints();
            switch (movcnt)
            {
                case 0:
                    if (!Directrix.NewSlope.AlmostEqual(Directrix.Slope))
                    {
                        throw new PointMovingException(this, string.Format(Resources.Translate("FocusCannotMove"), Name));
                    }
                    break;
                case 1:
                case 2:
                    PointBase fix, move;
                    if (FocusPoint2.Movable)
                    {
                        move = FocusPoint2;
                        fix = FocusPoint1;
                    }
                    else
                    {
                        move = FocusPoint1;
                        fix = FocusPoint2;
                    }
                    double[] abc = fix.NewXY.GetABC(-1 / Directrix.NewSlope);
                    PointD[] pts = CurveIntersection.StraightLineCircleIntersection(abc[0], abc[1], abc[2], fix.NewXY, fix.NewXY.DistanceTo(move.NewXY));
                    if (pts != null)
                    {
                        unlimit = fix.TempMoveLock = true;
                        try
                        {
                            move.Move(Pad.XYToPxy(move.NewXY.GetNearestPoint(pts)));
                        }
                        finally
                        {
                            unlimit = fix.TempMoveLock = false;
                        }
                    }
                    break;
            }
        }
        public override PointCollectionBase[] GetInPointMovingRange(PointBase movePoint, Base sender = null)
        {
            switch (CountMovablePoints())
            {
                case 1:
                    if (unlimit) return Universal.GenerateInstanceArray();
                    PointD proj = (movePoint == FocusPoint1 ? FocusPoint2 : FocusPoint1).NewXY.GetProjectivePoint(Directrix.NewA, Directrix.NewB, Directrix.NewC);
                    return new PointCollectionBase[] { new Ray(proj, proj.GetAngle((movePoint == FocusPoint1 ? FocusPoint2 : FocusPoint1).NewXY)) };
            }
            return base.GetInPointMovingRange(movePoint, sender);
        }
        public override void ComponentBuilding(Base component)
        {
            if (OrderIndex == 0)
            {
                if (FocusPoint1 == null) return;
                if (FocusPoint2 != null)
                {
                    double p = Math.Min(FocusPoint1.DistanceTo(Directrix), FocusPoint2.DistanceTo(Directrix));
                    ImaginaryAxisHalfLength = Math.Sqrt(p * c);
                    RealAxisHalfLength = Math.Sqrt(c * c - ImaginaryAxisHalfLength * ImaginaryAxisHalfLength);
                }
                else
                {
                    pske.Center = (FocusPoint1.Pxy + SpareCoordinate) / 2;
                    PointD xy = Pad.PxyToXY(SpareCoordinate);
                    ske.Center = (FocusPoint1.ToPointD() + xy) / 2;
                    c = Center.DistanceTo(xy);
                    FocusAngle = FocusPoint1.ToPointD().GetAngle(xy);
                    ShowStatus($"{Resources.Translate("Creating")}{Resources.ColonSeparate}{Name}{Resources.LeftParenthesis}{Resources.JoinLocalizedString("Hyperbola", "Center")}{Resources.ColonSeparate}{Pad.ShowPoint(Center)}{Resources.CommaSeparate}{Resources.Translate("FocalHalfLength")} = {FocalHalfLength}）");
                    return;
                }
            }
            else
            {
                if (FocusPoint1 == null) return;
                PointD xy = Pad.PxyToXY(SpareCoordinate);
                xy = xy.GetProjectivePoint(FocusPoint1.ToPointD(), -1 / Directrix.Slope);
                ske.Center = (FocusPoint1.ToPointD() + xy) / 2;
                c = Center.DistanceTo(xy);
                FocusAngle = FocusPoint1.ToPointD().GetAngle(xy);
                double p = Math.Min(FocusPoint1.DistanceTo(Directrix), xy.DistanceTo(Directrix.A, Directrix.B, Directrix.C));
                if (Pad.Coordinate != null) xy = Pad.Coordinate.XYToPxy(xy);
                pske.Center = (FocusPoint1.Pxy + xy) / 2;
                SpareCoordinate = xy;
                ImaginaryAxisHalfLength = Math.Sqrt(p * c);
                RealAxisHalfLength = Math.Sqrt(c * c - ImaginaryAxisHalfLength * ImaginaryAxisHalfLength);
                ShowStatus($"{Resources.Translate("Creating")}{Resources.ColonSeparate}{Name}");
            }
            base.ComponentBuilding(component);
        }
        public override bool IsCreationCompleted
        {
            get
            {
                return base.IsCreationCompleted && Directrix != null && Directrix.IsCreationCompleted;
            }
        }
        public override bool CanDrawVirtual
        {
            get
            {
                if (OrderIndex == 0)
                {
                    if (FocusPoint2 != null)
                    {
                        return true;
                    }
                }
                else
                {
                    if (FocusPoint1 != null)
                    {
                        return true;
                    }
                }
                return false;
            }
        }
        public override void CreateComponents()
        {
            if (FocusPoint1 == null)
            {
                FocusPoint1 = Pad.CreatePoint(string.Format("{0}1", Resources.Translate("Focus")), SpareCoordinate_, true, Directrix == null ? Universal.GenerateInstanceArray() : new PointCollectionBase[] { new Universal(Directrix.Location, Directrix.Slope) });
                Pad.TrackObj = this;
            }
            else if (FocusPoint2 == null)
            {
                PointCollectionBase pc;
                if (Directrix == null)
                {
                    pc = new Universal();
                }
                else
                {
                    PointD proj = FocusPoint1.ToPointD().GetProjectivePoint(Directrix.A, Directrix.B, Directrix.C);
                    pc = new Ray(proj, proj.GetAngle(FocusPoint1.ToPointD()) + Math.PI);
                }
                pc.Exceptions = new HashSet<PointCollectionBase>() { new DiscontinuityPoint(FocusPoint1.ToPointD()) };
                FocusPoint2 = Pad.CreatePoint(string.Format("{0}2", Resources.Translate("Focus")), SpareCoordinate_, true, new PointCollectionBase[] { pc });
                if (Directrix == null)
                {
                    Pad.TrackObj = Directrix = new ConstrainedStraightLine(Pad, OfTranslator("Directrix"),
                        (-1 / FocusPoint1.ToPointD().GetSlope(FocusPoint2.ToPointD())).ToString(), false)
                    { SpareCoordinate_ = SpareCoordinate_, };
                }
                else
                {
                    base.CreateComponents();
                }
            }
            else
            {
                ConstrainedStraightLine sl = Directrix as ConstrainedStraightLine;
                sl.CreateComponents(Pad.CreatePoint(Resources.Translate("AnchorPoint"), sl.SpareCoordinate_, true,
                new PointCollectionBase[] { new CustomCollection((PointD pt)=>
                {
                    double k=-1/FocusPoint1.GetSlope(FocusPoint2), d1=pt.DistanceTo(FocusPoint1.ToPointD(),k),
                    d2=pt.DistanceTo(FocusPoint2.ToPointD(),k);
                    return pt.GetProjectivePoint((d1<d2?FocusPoint1:FocusPoint2).ToPointD(),k);
                },
                (PointD pt)=> { return Utility.ScalarProduct(FocusPoint2.ToPointD(),FocusPoint1.ToPointD(),FocusPoint2.ToPointD(),pt)>0 &&
                    Utility.ScalarProduct(FocusPoint1.ToPointD(),FocusPoint2.ToPointD(),FocusPoint1.ToPointD(),pt)>0; })}));
                base.CreateComponents();
            }
        }
        //public override void Paint(Graphics gra)
        //{
        //    base.Paint(gra);
        //    if (Directrix != null && Directrix.LayerItem == null)
        //    {
        //        Directrix.Paint(gra);
        //    }
        //}
    }
}
